from fileServer import fileServer
from networkNode import NetworkNode
from optionServer import optionServer
import time
import pickle

class NetworkServer(NetworkNode):
    def __init__(self):
        NetworkNode.__init__(self, optionServer.get('ip', None), optionServer.get('port',1235))
        
        self.server = (optionServer.get('serverIp',self.ip), optionServer.get('serverPort',1234))
        
        self.connected = False
        
        self.log("connecting to", self.server)
        self.send("reg", fileServer.dirs)
        self.wait(lambda: not self.connected, self.pump, lambda: self.send("reg", fileServer.dirs))
        
        self.log("connected!")
        
        self.stateNames = None
        self.send("getStateNames",None)
        self.wait(lambda: self.stateNames == None, self.pump, lambda: self.send("getStateNames",None))

        self.log("loading %d objects..." % len(self.stateNames))
        
        def retryLoad():
            for name in self.stateNames:
                self.send("regetState",name)
        
        self.send("getState",None)
        self.wait(lambda: self.stateNames, self.pump, retryLoad)
        
        self.log("done loading")
        
        self.objectNames = {}
        self.requestedVars = []
        self.rpcResults = {}
        self.actionNames = {}
        self.stateCache = {}
        self.stateCacheStart = time.time()
        
    def kill(self):
        self.log("disconnecting...")
        self.send("unreg",None)
        if optionServer.get('waitForUnregAck',False):
            self.wait(lambda: self.connected, self.pump, lambda: self.send("unreg",None))
        self.log("disconnected!")
        NetworkNode.kill(self)
        
    def send(self, cmd, msg, dest = None):
        NetworkNode.send(self, cmd, msg, dest or self.server)
        
    def batchSend(self, cmd, msg, dest = None):
        NetworkNode.batchSend(self, cmd, msg, dest or self.server)
        
    def receivePacket_stateNames(self, msg, sender):
        self.stateNames = msg
        
    def receivePacket_statePart(self, msg, sender):
        name, vals = msg
        if name in self.stateNames:
            self.stateNames.remove(name)
            obj = Object(name, fromServer = True)
            obj.vars.update(vals) #not sent through constructor because we don't want to echo back to the server
            map(obj.dirtyVars.add,vals.keys())
        
    def receivePacket_regack(self, msg, sender):
        self.connected = True
        
    def receivePacket_unregack(self, msg, sender):
        self.connected = False
        
    def makeObjectName(self, baseName):
        self.send("makeObject",baseName)
        self.objectNames.setdefault(baseName,[])
        self.wait(lambda: not self.objectNames[baseName], self.pump, lambda: self.send("makeObject", baseName))
        return self.objectNames[baseName].pop()
        
    def receivePacket_objectName(self, msg, sender):
        baseName, name = msg
        self.objectNames[baseName].append(name)
        
    def receivePacket_deleteObject(self, msg, sender):
        Object.deleteObjectByName(msg)
        
    def setVar(self, objectName, varName, val):
        if optionServer.get("batchSetVars",False):
            self.stateCache.setdefault(objectName,{})[varName] = val
            currentTime = time.time()
            if currentTime - self.stateCacheStart > optionServer.get("batchSetVarTime",0.01):
                self.stateCacheStart = currentTime
                self.log("send %d cached state vars" % sum([len(objVars) for objVars in self.stateCache.values()]))
                self.send("updateState",self.stateCache)
                self.stateCache.clear()
        else:
            self.send("setVar",(objectName,varName,val))
        
    def distSetVar(self, objectName, varName, val):
        self.send("distSetVar",(objectName,varName,val))
        
    def requestVar(self, objectName, varName):
        if (objectName, varName) not in self.requestedVars:
            def doRequestVar():
                self.send("requestVar",(objectName,varName))
            doRequestVar()
            self.wait(lambda: (objectName,varName) not in self.requestedVars, self.pump, doRequestVar)
        
    def receivePacket_requestVarAck(self, msg, sender):
        self.requestedVars.append(msg)
        
    def receivePacket_setVar(self, msg, sender):
        #self.log("receive_setVar",msg)
        objectName, varName, val = msg
        assert objectName in Object._objects, "cannot set var %s on unknown object %s" % (varName,objectName)
        obj = Object._objects[objectName]
        obj.vars[varName] = val
        if varName not in obj.dirtyVars:
            obj.dirtyVars.add(varName)
            
    def receivePacket_updateState(self, msg, sender):
        for objectName, objectVars in msg.iteritems():
            Object.getObject(objectName).vars.update(objectVars)
            
    def runAction(self, objectName, actionType, actionName, args):
        def doRunAction():
            self.send("runAction",(objectName,actionType,actionName, args))
        doRunAction()
        self.wait(lambda: (objectName, actionType, actionName) not in self.actionNames, self.pump, doRunAction, 10)
        return self.actionNames.pop((objectName, actionType, actionName))
        
    def receivePacket_actionName(self, msg, sender):
        objectName, actionType, actionDesiredName, actionName = msg
        self.actionNames[(objectName,actionType,actionDesiredName)] = actionName
    
    def receivePacket_runAction(self, msg, sender):
        self.log("receive_runAction",msg)
        objectName, actionType, actionName, args = msg
        Object.getObject(objectName).doRunAction(actionType,actionName,args)
        
    def receivePacket_makeObject(self, msg, sender):
        Object(msg,fromServer = True)
        self.send("makeObjectAck",None)
        
    def rpc(self, objectName, actionName, cmd, args):
        return self.defer(objectName,actionName,args)()
        
    def defer(self, objectName, actionName, cmd, args):
        def doRpc():
            self.send("rpc",(objectName,actionName,cmd,args))
        doRpc()
        def closure():
            self.wait(lambda: not self.rpcResults.get((objectName,actionName,cmd)), self.pump, doRpc)
            return self.rpcResults[(objectName,actionName,cmd)].pop()
        return closure
        
    def receivePacket_rpc(self, msg, sender):
        objectName, actionName, cmd, args = msg
        assert Object.hasObject(objectName)
        object = Object.getObject(objectName)
        assert actionName in object.actions
        action = object.actions[actionName]
        assert cmd in action.receiveFunctions
        self.send("rpcResult",(objectName,actionName,cmd,action.receiveFunctions[cmd](args)))
        
    def receivePacket_rpcResult(self, msg, sender):
        objectName, actionName, cmd, result = msg
        self.rpcResults.setdefault((objectName,actionName,cmd),[]).append(result)
        
    def receivePacket_publish(self, msg, sender):
        from action import Action
        cmd, args = msg
        Action.doPublish(cmd,args)
        
    def killNetwork(self):
        self.killedNetwork = False
        self.send("killNetwork",None)
        self.wait(lambda: not self.killedNetwork, self.pump, lambda: self.send("killNetwork",None))
        
    def receivePacket_killNetwork(self, msg, sender):
        self.killedNetwork = True
        self.killAll()
        
from object import Object

networkServer = NetworkServer()
networkServer()
